using System.Collections.Generic;
using Framework.Core;

namespace Framework
{
    public class FsmState
    {
        public string Name { get; private set; }
        public Fsm Fsm { get; private set; }
        public List<FsmTransition> Transitions = new List<FsmTransition>();
        public float StateTime { get; private set; }
        private readonly Dictionary<string, FsmTransition> transitions = new Dictionary<string, FsmTransition>();
        private readonly List<IFsmStateAction> actions = new List<IFsmStateAction>();
        private List<IFsmStateAction> activeActions;
        private int activeActionIndex;
        private bool initialized;
        private bool finished;
        private object _sign;
        public FsmState()
        {
        }
        public void Initialize(object sign, string name)
        {
            if (sign == null) return;
            if (_sign != null && _sign != sign) return;
            this._sign = sign;
            this.Name = name;
        }
        public void SetName(string value)
        {
            this.Name = value;
        }
        private FsmTransition AddTransition(FsmTransition value)
        {
            if (value == null) return null;
            if (initialized)
            {
                return null;
            }
            Transitions.Add(value);
            return value;
        }
        public FsmTransition AddTransition(string stateName, string eventName = null)
        {
            if (string.IsNullOrEmpty(eventName))
            {
                eventName = stateName;
            }
            var transition = new FsmTransition();
            transition.Initialize(stateName, eventName);
            return AddTransition(transition);
        }
        public FsmTransition GetTransition(string name)
        {
            transitions.TryGetValue(name, out FsmTransition value);
            return value;
        }
        public IFsmStateAction AddAction(IFsmStateAction value)
        {
            if (value != null)
            {
                actions.Add(value);
            }
            return value;
        }
        public bool Initialized
        {
            get
            {
                return this.initialized;
            }
        }
        public void InitData(Fsm fsm)
        {
            if (initialized)
            {
                return;
            }
            this.initialized = true;
            this.Fsm = fsm;
            transitions.Clear();
            foreach (var item in Transitions)
            {
                if (string.IsNullOrEmpty(item.EventName)) continue;
                if (item.ToFsmState == null)
                {
                    FrameworkLog.LogErrorFormat("{0} not find transition state {1}", item.EventName, item.ToState);
                    continue;
                }
                if (!transitions.ContainsKey(item.EventName))
                {
                    transitions.Add(item.EventName, item);
                }
            }
            Transitions.Clear();
            foreach (var action in actions)
            {
                action.Init(fsm);
            }
            activeActions = new List<IFsmStateAction>(actions.Count);
            this.StateTime = -1f;
            OnAwake();
        }
        public void Enter(object sign)
        {
            if (_sign != null && _sign != sign) return;
            if (this.StateTime > 0f) return;
            foreach (var item in actions)
            {
                item.Awake();
            }
            finished = false;
            OnEnter();
            activeActionIndex = 0;
            this.activeActions.Clear();
            ActiveAction();
            CheckAllActionsFinished();
            this.StateTime = 0f;
        }

        public void Update(float deltaTime, float progress, object sign)
        {
            if (this.StateTime < 0f) return;
            if (_sign != null && _sign != sign) return;
            this.StateTime += deltaTime;
            OnUpdate(deltaTime, progress);
            var len = this.activeActions.Count;
            while (len-- > 0)
            {
                var action = this.activeActions[len];
                action.Update(deltaTime, progress);
                if (action.Finished)
                {
                    activeActions.RemoveAt(len);
                    action.Exit();
                    OnActionFinished(action);
                }
            }
            CheckAllActionsFinished();
        }
        public void Exit(object sign)
        {
            if (this.StateTime < 0f) return;
            if (_sign != null && _sign != sign) return;
            var len = this.activeActions.Count;
            while (len-- > 0)
            {
                var action = this.activeActions[len];
                action.Exit();
            }
            activeActions.Clear();
            finished = true;
            OnExit();
            this.StateTime = -1f;
        }
        protected virtual void OnAwake()
        {

        }
        protected virtual void OnEnter()
        {

        }
        protected virtual void OnUpdate(float deltaTime, float progress)
        {

        }
        protected virtual void OnExit()
        {

        }
        protected virtual void OnActionFinished(IFsmStateAction value)
        {

        }
        private void CheckAllActionsFinished()
        {
            if (this.finished)
            {
                return;
            }
            if (this.activeActions.Count > 0) return;
            if (activeActionIndex >= actions.Count)
            {
                this.finished = true;
                this.Fsm.SendEvent(Fsm.EventActionFinish);
                return;
            }
            ActiveAction();
        }
        private void ActiveAction()
        {
            int count = actions.Count;
            if (activeActionIndex >= count) return;
            if (this.activeActions.Count < 1)
            {
                for (int i = activeActionIndex; i < count; i++)
                {
                    var action = actions[i];
                    activeActions.Insert(0, action);
                    action.Enter();
                    activeActionIndex++;
                    if (action.WatingFinish)
                    {
                        break;
                    }
                }
            }
        }

    }
}